package com.example.demo.annotation;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.expression.EvaluationContext;

import java.lang.reflect.Method;
import java.util.concurrent.locks.Lock;

/**
 * @Auther Created by l.wang on 2019/1/15.
 */
@Aspect
public class DistributedLockAdvice {
    private static String LOCK_PREFIX = "OVERALL_LOCK";
    private final ExpressionEvaluator evaluator = new ExpressionEvaluator();
    private IDistributedLock distributedLock = null;

    public DistributedLockAdvice(IDistributedLock distributedLock) {
        this.distributedLock = distributedLock;
    }

    /**
     * 获取方法及参数
     * @param keyExpression 注解中的参数 ("'test' + #id")
     * @param method 方法
     * @param args 参数
     * @param target
     * @param targetClass 加注解的类
     * @return
     */
    private Object generateKey(String keyExpression, Method method, Object args[], Object target, Class<?> targetClass) {
        EvaluationContext evaluationContext = evaluator.createEvaluationContext(method, args, target, targetClass);
        return evaluator.key(keyExpression, new AnnotatedElementKey(method, targetClass), evaluationContext);
    }

    @Around("@annotation(lockAnnotation)")//拦截注解
    public Object lock(ProceedingJoinPoint pjp, DistributedLock lockAnnotation) throws Throwable {
        MethodSignature sig = (MethodSignature) pjp.getSignature(); //当前执行的方法
        //获取方法中的参数
        String lockKey = LOCK_PREFIX + generateKey(lockAnnotation.value(), sig.getMethod(), pjp.getArgs(), pjp.getTarget(), pjp.getTarget().getClass());
        Lock lock = null;
        try {
            try {
                //对此方法中的参数进行加锁
                lock = distributedLock.lock(lockKey);
            } catch (Exception ignore) {
                return pjp.proceed();
            }
            return pjp.proceed();
        } finally {//最终执行解锁
            if (lock != null) {
                lock.unlock();
            }
        }
    }
}

